[Chapter Ten][Previous]
[Next] [Art of
Assembly][Randall Hyde]
Art of Assembly: Chapter Ten
- 10.6 - Loops
- 10.6.1 - While Loops
- 10.6.2 - Repeat..Until Loops
- 10.6.3 - LOOP..ENDLOOP Loops
- 10.6.4 - FOR Loops
- 10.7 - Register Usage and Loops
10.6 Loops
Loops represent the final basic control structure (sequences, decisions,
and loops) which make up a typical program. Like so many other structures
in assembly language, you'll find yourself using loops in places you've
never dreamed of using loops. Most HLLs have implied loop structures hidden
away. For example, consider the BASIC statement IF A$ = B$ THEN 100
.
This if
statement compares two strings and jumps to statement
100 if they are equal. In assembly language, you would need to write a loop
to compare each character in A$
to the corresponding character
in B$
and then jump to statement 100 if and only if all the
characters matched. In BASIC, there is no loop to be seen in the program.
In assembly language, this very simple if
statement requires
a loop. This is but a small example which shows how loops seem to pop up
everywhere.
Program loops consist of three components: an optional initialization component,
a loop termination test, and the body of the loop. The order with which
these components are assembled can dramatically change the way the loop
operates. Three permutations of these components appear over and over again.
Because of their frequency, these loop structures are given special names
in HLLs: while
loops, repeat..until
loops (do..while
in C/C++), and loop..endloop
loops.
10.6.1 While Loops
The most general loop is the while loop. It takes the following form:
WHILE boolean expression DO statement;
There are two important points to note about the while loop. First, the
test for termination appears at the beginning of the loop. Second as a direct
consequence of the position of the termination test, the body of the loop
may never execute. If the termination condition always exists, the loop
body will always be skipped over.
Consider the following Pascal while loop:
I := 0;
WHILE (I<100) do I := I + 1;
I := 0;
is the initialization code for this loop. I is a loop
control variable, because it controls the execution of the body of the loop.
(I<100)
is the loop termination condition. That is, the loop will
not terminate as long as I is less than 100. I:=I+1;
is the
loop body. This is the code that executes on each pass of the loop. You
can convert this to 80x86 assembly language as follows:
mov I, 0
WhileLp: cmp I, 100
jge WhileDone
inc I
jmp WhileLp
WhileDone:
Note that a Pascal while loop can be easily synthesized using an if and
a goto statement. For example, the Pascal while loop presented above can
be replaced by:
I := 0;
1: IF (I<100) THEN BEGIN
I := I + 1;
GOTO 1;
END;
More generally, any while loop can be built up from the following:
optional initialization code
1: IF not termination condition THEN BEGIN
loop body
GOTO 1;
END;
Therefore, you can use the techniques from earlier in this chapter to convert
if
statements to assembly language. All you'll need is an additional
jmp
(goto
) instruction.
10.6.2 Repeat..Until Loops
The repeat..until (do..while)
loop tests for the termination
condition at the end of the loop rather than at the beginning. In Pascal,
the repeat..until
loop takes the following form:
optional initialization code
REPEAT
loop body
UNTIL termination condition
This sequence executes the initialization code, the loop body, then tests
some condition to see if the loop should be repeated. If the boolean expression
evaluates to false, the loop repeats; otherwise the loop terminates. The
two things to note about the repeat..until
loop is that the
termination test appears at the end of the loop and, as a direct consequence
of this, the loop body executes at least once.
Like the while
loop, the repeat..until
loop can
be synthesized with an if
statement and a goto
.
You would use the following:
initialization code
1: loop body
IF NOT termination condition THEN GOTO 1
Based on the material presented in the previous sections, you can easily
synthesize repeat..until
loops in assembly language.
10.6.3 LOOP..ENDLOOP Loops
If while
loops test for termination at the beginning of
the loop and repeat..until
loops check for termination at the
end of the loop, the only place left to test for termination is in the middle
of the loop. Although Pascal and C/C++[4] don't
directly support such a loop, the loop..endloop
structure can
be found in HLL languages like Ada. The loop..endloop
loop
takes the following form:
LOOP
loop body
ENDLOOP;
Note that there is no explicit termination condition. Unless otherwise provided
for, the loop..endloop
construct simply forms an infinite loop.
Loop termination is handled by an if
and goto
statement[5].
Consider the following (pseudo) Pascal code which employs a loop..endloop
construct:
LOOP
READ(ch)
IF ch = '.' THEN BREAK;
WRITE(ch);
ENDLOOP;
In real Pascal, you'd use the following code to accomplish this:
1:
READ(ch);
IF ch = '.' THEN GOTO 2; (* Turbo Pascal supports BREAK! *)
WRITE(ch);
GOTO 1
2:
In assembly language you'd end up with something like:
LOOP1: getc
cmp al, '.'
je EndLoop
putc
jmp LOOP1
EndLoop:
10.6.4 FOR Loops
The for
loop is a special form of the while
loop
which repeats the loop body a specific number of times. In Pascal, the for
loop looks something like the following:
FOR var := initial TO final DO stmt
or
FOR var := initial DOWNTO final DO stmt
Traditionally, the for
loop in Pascal has been used to process
arrays and other objects accessed in sequential numeric order. These loops
can be converted directly into assembly language as follows:
In Pascal:
FOR var := start TO stop DO stmt;
In Assembly:
mov var, start
FL: mov ax, var
cmp ax, stop
jg EndFor
; code corresponding to stmt goes here.
inc var
jmp FL
EndFor:
Fortunately, most for loops repeat some statement(s) a fixed number of times.
For example,
FOR I := 0 to 7 do write(ch);
In situations like this, it's better to use the 80x86 loop instruction (or
corresponding dec cx/jne sequence) rather than simulate a for loop:
mov cx, 7
LP: mov al, ch
call putc
loop LP
Keep in mind that the loop instruction normally appears at the end of a
loop whereas the for loop tests for termination at the beginning of the
loop. Therefore, you should take precautions to prevent a runaway loop in
the event cx is zero (which would cause the loop instruction to repeat the
loop 65,536 times) or the stop value is less than the start value. In the
case of
FOR var := start TO stop DO stmt;
assuming you don't use the value of var within the loop, you'd probably
want to use the assembly code:
mov cx, stop
sub cx, start
jl SkipFor
inc cx
LP: stmt
loop LP
SkipFor:
Remember, the sub and cmp instructions set the flags in an identical fashion.
Therefore, this loop will be skipped if stop is less than start. It will
be repeated (stop-start)+1 times otherwise. If you need to reference the
value of var within the loop, you could use the following code:
mov ax, start
mov var, ax
mov cx, stop
sub cx, ax
jl SkipFor
inc cx
LP: stmt
inc var
loop LP
SkipFor:
The downto
version appears in the exercises.
10.7 Register Usage and Loops
Given that the 80x86 accesses registers much faster than memory locations,
registers are the ideal spot to place loop control variables (especially
for small loops). This point is amplified since the loop
instruction
requires the use of the cx
register. However, there are some
problems associated with using registers within a loop. The primary problem
with using registers as loop control variables is that registers are a limited
resource. In particular, there is only one cx
register. Therefore,
the following will not work properly:
mov cx, 8
Loop1: mov cx, 4
Loop2: stmts
loop Loop2
stmts
loop Loop1
The intent here, of course, was to create a set of nested loops, that is,
one loop inside another. The inner loop (Loop2
) should repeat
four times for each of the eight executions of the outer loop (Loop1
).
Unfortunately, both loops use the loop
instruction. Therefore,
this will form an infinite loop since cx
will be set to zero
(which loop
treats like 65,536) at the end of the first loop
instruction. Since cx
is always zero upon encountering the
second loop
instruction, control will always transfer to the
Loop1
label. The solution here is to save and restore the cx
register or to use a different register in place of cx
for
the outer loop:
mov cx, 8
Loop1: push cx
mov cx, 4
Loop2: stmts
loop Loop2
pop cx
stmts
loop Loop1
or:
mov bx, 8
Loop1: mov cx, 4
Loop2: stmts
loop Loop2
stmts
dec bx
jnz Loop1
Register corruption is one of the primary sources of bugs in loops in assembly
language programs, always keep an eye out for this problem.
[4] Technically, C/C++ does support such a
loop. "for(;;)" along with break provides this capability.
[5] Many high level languages use statements like
NEXT, BREAK, CONTINUE, EXIT, and CYCLE rather than GOTO; but they're all
forms of the GOTO statement.
- 10.6 - Loops
- 10.6.1 - While Loops
- 10.6.2 - Repeat..Until Loops
- 10.6.3 - LOOP..ENDLOOP Loops
- 10.6.4 - FOR Loops
- 10.7 - Register Usage and Loops
Art of Assembly: Chapter Ten - 27 SEP 1996
[Chapter Ten][Previous]
[Next] [Art of
Assembly][Randall Hyde]